package com.neusoft.bizcore.schedule.service.impl;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.quartz.CalendarIntervalScheduleBuilder;
import org.quartz.CalendarIntervalTrigger;
import org.quartz.CronScheduleBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import com.neusoft.bizcore.schedule.converter.JobConverter;
import com.neusoft.bizcore.schedule.dto.JobDetailDTO;
import com.neusoft.bizcore.schedule.service.JobMgrService;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;

import lombok.extern.slf4j.Slf4j;

/**
 * description
 *
 * @author sunchf
 * @date 2018年12月26日 下午2:09:32
 */
@Slf4j
@Service
public class JobMgrServiceImpl implements JobMgrService {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    @Autowired
    private JobConverter jobConverter;

    @Override
    public boolean isExists(final String name, final String gruop) {
        boolean result = false;
        final JobKey jobKey = JobKey.jobKey(name, gruop);
        try {
            result = this.schedulerFactoryBean.getScheduler().checkExists(jobKey);
        } catch (final SchedulerException e) {
            JobMgrServiceImpl.log.error(e.getMessage(), e);
        }

        return result;
    }

    @Override
    public List<JobKey> getJobs(final Searchable searchable) {
        // 查询所有的JobKey
        Set<JobKey> jobKeySet;
        try {
            jobKeySet = this.schedulerFactoryBean.getScheduler().getJobKeys(null);
        } catch (final SchedulerException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        final List<JobKey> jobKeys = jobKeySet.stream().filter(jobKey -> {
            if (searchable.hasKey("keyword")) {
                return this.matches(searchable.getStrValue("keyword"), jobKey);
            } else {
                return true;
            }
        }).sorted((key1, key2) -> key1.compareTo(key2)).collect(Collectors.toList());

        return jobKeys;
    }

    @Override
    public Page<JobKey> getJobs(final Searchable searchable, final Pageable pageable) {
        // 查询所有的JobKey
        Set<JobKey> jobKeySet;
        try {
            jobKeySet = this.schedulerFactoryBean.getScheduler().getJobKeys(null);
        } catch (final SchedulerException e) {
            throw new RuntimeException(e.getMessage(), e);
        }

        final List<JobKey> jobKeys = jobKeySet.stream().filter(jobKey -> {
            if (searchable.hasKey("keyword")) {
                return this.matches(searchable.getStrValue("keyword"), jobKey);
            } else {
                return true;
            }
        }).sorted((key1, key2) -> key1.compareTo(key2)).collect(Collectors.toList());

        // 分页
        final List<JobKey> pageContent = jobKeys.stream().skip(pageable.getOffset()).limit(pageable.getPageSize())
                .collect(Collectors.toList());
        return new PageImpl<>(pageContent, pageable, jobKeys.size());
    }

    @Override
    public JobDetailDTO getJobDetail(final String name) {
        return this.getJobDetail(name, null);
    }

    @Override
    public JobDetailDTO getJobDetail(final String name, final String group) {
        final JobKey job = JobKey.jobKey(name, name);
        return this.jobConverter.toDTO(job);
    }

    @Override
    public boolean pauseJob(final String name) {
        return this.pauseJob(name, null);
    }

    @Override
    public boolean pauseJob(final String name, final String group) {
        boolean result = false;
        final JobKey jobKey = JobKey.jobKey(name, group);
        try {
            this.schedulerFactoryBean.getScheduler().pauseJob(jobKey);
            result = true;
        } catch (final SchedulerException e) {
            new UnitedException("pauseJob", e);
        }
        return result;
    }

    @Override
    public boolean resumeJob(final String name) {
        return this.resumeJob(name, null);
    }

    @Override
    public boolean resumeJob(final String name, final String group) {
        boolean result = false;
        final JobKey jobKey = JobKey.jobKey(name, group);
        try {
            this.schedulerFactoryBean.getScheduler().resumeJob(jobKey);
            result = true;
        } catch (final SchedulerException e) {
            new UnitedException("resumeJob", e);
        }
        return result;
    }

    @Override
    public boolean deleteJob(final String name) {
        return this.deleteJob(name, null);
    }

    @Override
    public boolean deleteJob(final String name, final String group) {
        boolean result = false;
        final JobKey jobKey = JobKey.jobKey(name, group);
        try {
            this.schedulerFactoryBean.getScheduler().deleteJob(jobKey);
            result = true;
        } catch (final SchedulerException e) {
            new UnitedException("deleteJob", e);
        }
        return result;
    }

    @Override
    public Page<Trigger> getTriggers(final Searchable searchable, final Pageable pageable) {
        try {
            final List<String> triggerGroupNames = this.schedulerFactoryBean.getScheduler().getTriggerGroupNames();

            final Set<TriggerKey> allTriggerSet = new TreeSet<>();

            triggerGroupNames.stream().forEach(it -> {
                try {
                    final Set<TriggerKey> triggerKeys =
                            this.schedulerFactoryBean.getScheduler().getTriggerKeys(GroupMatcher.groupEquals(it));
                    allTriggerSet.addAll(triggerKeys);
                } catch (final SchedulerException e) {
                    JobMgrServiceImpl.log.error(e.getMessage(), e);
                }
            });

            final List<TriggerKey> jobKeys = allTriggerSet.stream().filter(triggerKey -> {
                if (searchable.hasKey("keyword")) {
                    return triggerKey.getName().toLowerCase().contains(searchable.getStrValue("keyword").toLowerCase());
                } else {
                    return true;
                }
            }).sorted((key1, key2) -> key1.compareTo(key2)).collect(Collectors.toList());

            // 先分页
            final List<TriggerKey> pageTriggerKeys = jobKeys.stream().filter(t -> {
                return null != t;
            }).skip(pageable.getOffset()).limit(pageable.getPageSize()).collect(Collectors.toList());

            final List<Trigger> triggers = pageTriggerKeys.stream().map(k -> {
                try {
                    return this.schedulerFactoryBean.getScheduler().getTrigger(k);
                } catch (final SchedulerException e) {
                    JobMgrServiceImpl.log.error("", "");
                }
                return null;
            }).collect(Collectors.toList());

            return new PageImpl<>(triggers, pageable, jobKeys.size());
        } catch (final SchedulerException e) {
            JobMgrServiceImpl.log.error("", e);
            return null;
        }

    }

    @Override
    public boolean pauseTrigger(final String triggerKey) {
        final boolean result = false;
        final TriggerKey trigger = TriggerKey.triggerKey(triggerKey);
        try {
            this.schedulerFactoryBean.getScheduler().pauseTrigger(trigger);
        } catch (final SchedulerException e) {
            new UnitedException("pauseTrigger", e);
        }
        return result;
    }

    @Override
    public boolean resumeTrigger(final String triggerKey) {
        final boolean result = false;
        final TriggerKey trigger = TriggerKey.triggerKey(triggerKey);
        try {
            this.schedulerFactoryBean.getScheduler().resumeTrigger(trigger);
        } catch (final SchedulerException e) {
            new UnitedException("resumeTrigger", e);
        }
        return result;
    }

    @Override
    public boolean deleteTrigger(final String triggerKey) {
        final boolean result = false;
        final TriggerKey trigger = TriggerKey.triggerKey(triggerKey);
        try {
            this.schedulerFactoryBean.getScheduler().unscheduleJob(trigger);
        } catch (final SchedulerException e) {
            new UnitedException("deleteTrigger", e);
        }
        return result;
    }

    private boolean matches(final String search, final JobKey jobKey) {
        boolean matched = jobKey.getName().toLowerCase().contains(search.toLowerCase());
        if (!matched) {
            try {
                final String jobDescription = this.schedulerFactoryBean.getScheduler().getJobDetail(jobKey)
                        .getDescription();
                matched = ((jobDescription != null) && jobDescription.contains(search));
            } catch (final Exception e) {
            }
        }
        return matched;
    }

    @Override
    public boolean scheduleJob(final String name, final Long interval, final JobDetail jobDetail) {
        return this.scheduleJob(name, null, interval, jobDetail);
    }

    @Override
    public boolean scheduleJob(final String name, final String group, final Long interval, final JobDetail jobDetail) {
        return this.scheduleJob(name, group, interval, jobDetail, Trigger.DEFAULT_PRIORITY);
    }

    @Override
    public boolean scheduleJob(final String name, final Long interval, final JobDetail jobDetail, final int priority) {
        return this.scheduleJob(name, null, interval, jobDetail, priority);
    }

    @Override
    public boolean scheduleJob(final String name, final String group, final Long interval, final JobDetail jobDetail,
            int priority) {
        boolean result = false;
        if (priority > Trigger.DEFAULT_PRIORITY) {
            priority = Trigger.DEFAULT_PRIORITY;
        }
        final CalendarIntervalScheduleBuilder intervalScheduleBuilder = CalendarIntervalScheduleBuilder
                .calendarIntervalSchedule().withInterval(interval.intValue(), IntervalUnit.SECOND);
        final Trigger trigger =
                TriggerBuilder.newTrigger().withIdentity(name, group).withPriority(priority)
                        .withSchedule(intervalScheduleBuilder)
                        .build();
        try {
            this.schedulerFactoryBean.getScheduler().scheduleJob(jobDetail, trigger);
            result = true;
        } catch (final SchedulerException e) {
            new UnitedException("scheduleJob", e);
            JobMgrServiceImpl.log.error("构建定时任务异常", e);
        }
        return result;
    }

    @Override
    public boolean rescheduleJob(final String name, final Long interval) {
        return this.rescheduleJob(name, null, interval);
    }

    @Override
    public boolean rescheduleJob(final String name, final String group, final Long interval) {
        boolean result = false;
        final Scheduler scheduler = this.schedulerFactoryBean.getScheduler();
        try {
            final TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
            if (null != scheduler.getTrigger(triggerKey)) {

                final Trigger pTrigger = scheduler.getTrigger(triggerKey);
                if (pTrigger instanceof SimpleTrigger) {
                    final SimpleTrigger trigger = (SimpleTrigger) pTrigger;

                    // 采集周期发生变化更新定时任务
                    if ((interval * 1000) != trigger.getRepeatInterval()) {

                        final SimpleScheduleBuilder simpleScheduleBuilder =
                                SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(interval.intValue())
                                        .repeatForever();
                        final Trigger newTrigger = TriggerBuilder.newTrigger().withIdentity(name, group)
                                .withSchedule(simpleScheduleBuilder).build();
                        scheduler.rescheduleJob(triggerKey, newTrigger);
                    }
                } else if (pTrigger instanceof CalendarIntervalTrigger) {
                    final CalendarIntervalTrigger trigger = (CalendarIntervalTrigger) pTrigger;
                    // 采集周期发生变化更新定时任务
                    if ((interval * 1000) != trigger.getRepeatInterval()) {
                        final CalendarIntervalScheduleBuilder intervalScheduleBuilder = CalendarIntervalScheduleBuilder
                                .calendarIntervalSchedule().withInterval(interval.intValue(), IntervalUnit.SECOND);
                        final Trigger newTrigger = TriggerBuilder.newTrigger().withIdentity(name, group)
                                .withSchedule(intervalScheduleBuilder).build();
                        scheduler.rescheduleJob(triggerKey, newTrigger);
                    }
                }
                result = true;
            }
        } catch (final SchedulerException e) {
            new UnitedException("rescheduleJob", e);
            JobMgrServiceImpl.log.error("更新定时任务异常", e);
        }
        return result;
    }

    @Override
    public boolean scheduleCronJob(final String keyword, final String cronExpression, final JobDetail jobDetail) {
        boolean result = false;
        final CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule(cronExpression)
                .withMisfireHandlingInstructionDoNothing();
        final Trigger trigger = TriggerBuilder.newTrigger().withIdentity(keyword).withSchedule(builder).build();
        try {
            this.schedulerFactoryBean.getScheduler().scheduleJob(jobDetail, trigger);
            result = true;
        } catch (final SchedulerException e) {
            new UnitedException("scheduleJob", e);
            JobMgrServiceImpl.log.error("构建cron任务异常", e);
        }
        return result;
    }

}
